package com.bktech.service;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

import com.bktech.stupidnews.R;

public class ImageDownloader {
	
	private final String TAG = ImageDownloader.class.getName();
	private final HashMap<String, Bitmap> cache;
	Resources resources;
	Bitmap bitmap;

	public ImageDownloader(Context context) {
		cache = new HashMap<String, Bitmap>();
		resources = context.getResources();
		bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_temp);

	}

	public synchronized void download(String url, ImageView imageView) {
		Bitmap bitmap = getBitmapFromCache(url);
		if (bitmap == null) {
			forceDownload(url, imageView);
		} else {
			cancelPotentialDownload(url, imageView);
			imageView.setImageBitmap(bitmap);
		}
	}

	private Bitmap getBitmapFromCache(String url) {
		synchronized (cache) {
			final Bitmap bitmap = cache.get(url);
			return bitmap;
		}
	}

	private void forceDownload(String url, ImageView imageView) {
		if (url == null) {
			imageView.setImageDrawable(null);
			return;
		}
		if (cancelPotentialDownload(url, imageView)) {
			BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
			DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
					resources, bitmap, task);
			imageView.setImageDrawable(downloadedDrawable);
			imageView.setMinimumHeight(156);
			task.execute(url);
		}
	}

	private boolean cancelPotentialDownload(String url, ImageView imageView) {
		BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
		if (bitmapDownloaderTask != null) {
			String bitmapUrl = bitmapDownloaderTask.url;
			if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
				bitmapDownloaderTask.cancel(true);
			} else {
				// The same URL is already being downloaded.
				return false;
			}
		}
		return true;
	}

	class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {

		private String url;
		private final ImageView imageViewReference;

		public BitmapDownloaderTask(ImageView imageView) {
			this.imageViewReference = imageView;
		}

		@Override
		protected Bitmap doInBackground(String... params) {
			url = params[0];
			return downloadBitmap(url);
		}

		public Bitmap downloadBitmap(String url) {
			// AndroidHttpClient is not allowed to be used from the main thread
			final HttpClient client = AndroidHttpClient.newInstance("Android");
			final HttpGet getRequest = new HttpGet(url);

			try {
				HttpResponse response = client.execute(getRequest);
				final int statusCode = response.getStatusLine().getStatusCode();
				if (statusCode != HttpStatus.SC_OK) {
					Log.w("ImageDownloader", "Error " + statusCode
							+ " while retrieving bitmap from " + url);
					return null;
				}

				final HttpEntity entity = response.getEntity();
				if (entity != null) {
					InputStream inputStream = null;
					try {
						inputStream = entity.getContent();
						// return BitmapFactory.decodeStream(inputStream);
						// Bug on slow connections, fixed in future release.
						return BitmapFactory
								.decodeStream(new FlushedInputStream(
										inputStream));
					} finally {
						if (inputStream != null) {
							inputStream.close();
						}
						entity.consumeContent();
					}
				}
			} catch (IOException e) {
				getRequest.abort();
				Log.w(TAG, "I/O error while retrieving bitmap from " + url, e);
			} catch (IllegalStateException e) {
				getRequest.abort();
				Log.w(TAG, "Incorrect URL: " + url);
			} catch (Exception e) {
				getRequest.abort();
				Log.w(TAG, "Error while retrieving bitmap from " + url, e);
			} finally {
				if ((client instanceof AndroidHttpClient)) {
					((AndroidHttpClient) client).close();
				}
			}
			return null;
		}

		@Override
		protected void onPostExecute(Bitmap bitmap) {

			if (isCancelled()) {
				bitmap = null;
			}

			addBitmapToCache(url, bitmap);

			if (imageViewReference != null) {
				ImageView imageView = imageViewReference;
				BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
				imageView.setImageBitmap(bitmap);
			}
		}

	}

	private BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
		if (imageView != null) {
			Drawable drawable = imageView.getDrawable();
			if (drawable instanceof DownloadedDrawable) {
				DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
				return downloadedDrawable.getBitmapDownloaderTask();
			}
		}
		return null;
	}

	private void addBitmapToCache(String url, Bitmap bitmap) {
		cache.put(url, bitmap);
	}

	// class DownloadedDrawable extends ColorDrawable {
	// private final WeakReference<BitmapDownloaderTask>
	// bitmapDownloaderTaskReference;
	//
	// public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
	// super(Color.BLACK);
	// bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
	// bitmapDownloaderTask);
	// }
	//
	// public BitmapDownloaderTask getBitmapDownloaderTask() {
	// return bitmapDownloaderTaskReference.get();
	// }
	// }

	class DownloadedDrawable extends BitmapDrawable {
		private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

		public DownloadedDrawable(Resources res, Bitmap bitmap,
				BitmapDownloaderTask bitmapWorkerTask) {
			super(res, bitmap);
			bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
					bitmapWorkerTask);
		}

		public BitmapDownloaderTask getBitmapDownloaderTask() {
			return bitmapDownloaderTaskReference.get();
		}
	}

	class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;
			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);
				if (bytesSkipped == 0L) {
					int b = read();
					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}
				totalBytesSkipped += bytesSkipped;
			}
			return totalBytesSkipped;
		}
	}

}
